# 音频转文字服务监控模块
# 提供全面的性能监控、指标收集和状态追踪功能

import time
import asyncio
import logging
import threading
from typing import Dict, Any, List, Optional, Callable
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from collections import defaultdict, deque
from enum import Enum
import psutil
import json
from pathlib import Path

# 配置日志
logger = logging.getLogger(__name__)

class ProcessingStatus(Enum):
    """处理状态枚举"""
    PENDING = "pending"
    PROCESSING = "processing"
    COMPLETED = "completed"
    FAILED = "failed"
    CANCELLED = "cancelled"

class MetricType(Enum):
    """指标类型枚举"""
    COUNTER = "counter"
    GAUGE = "gauge"
    HISTOGRAM = "histogram"
    TIMER = "timer"

@dataclass
class ProcessingTask:
    """处理任务信息"""
    task_id: str
    filename: str
    file_size: int
    language: str
    model: str
    status: ProcessingStatus
    start_time: datetime
    end_time: Optional[datetime] = None
    processing_time: Optional[float] = None
    confidence: Optional[float] = None
    text_length: Optional[int] = None
    error_message: Optional[str] = None
    device: Optional[str] = None
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典格式"""
        return {
            "task_id": self.task_id,
            "filename": self.filename,
            "file_size": self.file_size,
            "language": self.language,
            "model": self.model,
            "status": self.status.value,
            "start_time": self.start_time.isoformat(),
            "end_time": self.end_time.isoformat() if self.end_time else None,
            "processing_time": self.processing_time,
            "confidence": self.confidence,
            "text_length": self.text_length,
            "error_message": self.error_message,
            "device": self.device
        }

@dataclass
class SystemMetrics:
    """系统性能指标"""
    timestamp: datetime
    cpu_percent: float
    memory_percent: float
    memory_used_mb: float
    memory_available_mb: float
    disk_usage_percent: float
    gpu_memory_used_mb: Optional[float] = None
    gpu_memory_total_mb: Optional[float] = None
    gpu_utilization_percent: Optional[float] = None
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典格式"""
        return {
            "timestamp": self.timestamp.isoformat(),
            "cpu_percent": self.cpu_percent,
            "memory_percent": self.memory_percent,
            "memory_used_mb": self.memory_used_mb,
            "memory_available_mb": self.memory_available_mb,
            "disk_usage_percent": self.disk_usage_percent,
            "gpu_memory_used_mb": self.gpu_memory_used_mb,
            "gpu_memory_total_mb": self.gpu_memory_total_mb,
            "gpu_utilization_percent": self.gpu_utilization_percent
        }

class PerformanceMonitor:
    """性能监控器"""
    
    def __init__(self, max_history_size: int = 1000):
        self.max_history_size = max_history_size
        self.tasks: Dict[str, ProcessingTask] = {}
        self.completed_tasks: deque = deque(maxlen=max_history_size)
        self.system_metrics: deque = deque(maxlen=max_history_size)
        self.metrics: Dict[str, Any] = defaultdict(list)
        self.counters: Dict[str, int] = defaultdict(int)
        self.gauges: Dict[str, float] = defaultdict(float)
        self.timers: Dict[str, List[float]] = defaultdict(list)
        self.error_stats: Dict[str, int] = defaultdict(int)
        
        # 监控配置
        self.monitoring_enabled = True
        self.system_metrics_interval = 30  # 秒
        self.metrics_retention_hours = 24
        
        # 启动系统监控
        self._start_system_monitoring()
        
        logger.info("性能监控器已启动")
    
    def _start_system_monitoring(self):
        """启动系统性能监控"""
        def monitor_system():
            while self.monitoring_enabled:
                try:
                    metrics = self._collect_system_metrics()
                    self.system_metrics.append(metrics)
                    self._cleanup_old_metrics()
                    time.sleep(self.system_metrics_interval)
                except Exception as e:
                    logger.error(f"系统监控错误: {e}")
                    time.sleep(self.system_metrics_interval)
        
        thread = threading.Thread(target=monitor_system, daemon=True)
        thread.start()
    
    def _collect_system_metrics(self) -> SystemMetrics:
        """收集系统性能指标"""
        # CPU和内存信息
        cpu_percent = psutil.cpu_percent(interval=1)
        memory = psutil.virtual_memory()
        disk = psutil.disk_usage('/')
        
        # GPU信息（如果可用）
        gpu_memory_used = None
        gpu_memory_total = None
        gpu_utilization = None
        
        try:
            import torch
            if torch.cuda.is_available():
                gpu_memory_used = torch.cuda.memory_allocated() / 1024 / 1024  # MB
                gpu_memory_total = torch.cuda.get_device_properties(0).total_memory / 1024 / 1024  # MB
                gpu_utilization = torch.cuda.utilization()
        except ImportError:
            pass
        except Exception as e:
            logger.debug(f"GPU监控失败: {e}")
        
        return SystemMetrics(
            timestamp=datetime.now(),
            cpu_percent=cpu_percent,
            memory_percent=memory.percent,
            memory_used_mb=memory.used / 1024 / 1024,
            memory_available_mb=memory.available / 1024 / 1024,
            disk_usage_percent=disk.percent,
            gpu_memory_used_mb=gpu_memory_used,
            gpu_memory_total_mb=gpu_memory_total,
            gpu_utilization_percent=gpu_utilization
        )
    
    def _cleanup_old_metrics(self):
        """清理过期的监控数据"""
        cutoff_time = datetime.now() - timedelta(hours=self.metrics_retention_hours)
        
        # 清理系统指标
        while self.system_metrics and self.system_metrics[0].timestamp < cutoff_time:
            self.system_metrics.popleft()
        
        # 清理完成的任务
        while (self.completed_tasks and 
               self.completed_tasks[0].start_time < cutoff_time):
            self.completed_tasks.popleft()
    
    def start_task(self, task_id: str, filename: str, file_size: int, 
                   language: str, model: str, device: str = None) -> ProcessingTask:
        """开始监控一个处理任务"""
        task = ProcessingTask(
            task_id=task_id,
            filename=filename,
            file_size=file_size,
            language=language,
            model=model,
            status=ProcessingStatus.PROCESSING,
            start_time=datetime.now(),
            device=device
        )
        
        self.tasks[task_id] = task
        self.increment_counter("tasks_started")
        self.set_gauge("active_tasks", len(self.tasks))
        
        logger.info(f"开始监控任务: {task_id} - {filename}")
        return task
    
    def complete_task(self, task_id: str, confidence: float = None, 
                     text_length: int = None) -> Optional[ProcessingTask]:
        """完成一个处理任务"""
        if task_id not in self.tasks:
            logger.warning(f"任务不存在: {task_id}")
            return None
        
        task = self.tasks[task_id]
        task.status = ProcessingStatus.COMPLETED
        task.end_time = datetime.now()
        task.processing_time = (task.end_time - task.start_time).total_seconds()
        task.confidence = confidence
        task.text_length = text_length
        
        # 移动到完成队列
        self.completed_tasks.append(task)
        del self.tasks[task_id]
        
        # 更新指标
        self.increment_counter("tasks_completed")
        self.set_gauge("active_tasks", len(self.tasks))
        self.add_timer("processing_time", task.processing_time)
        
        if confidence is not None:
            self.add_timer("confidence_score", confidence)
        
        logger.info(f"任务完成: {task_id} - 用时: {task.processing_time:.2f}s")
        return task
    
    def fail_task(self, task_id: str, error_message: str) -> Optional[ProcessingTask]:
        """标记任务失败"""
        if task_id not in self.tasks:
            logger.warning(f"任务不存在: {task_id}")
            return None
        
        task = self.tasks[task_id]
        task.status = ProcessingStatus.FAILED
        task.end_time = datetime.now()
        task.processing_time = (task.end_time - task.start_time).total_seconds()
        task.error_message = error_message
        
        # 移动到完成队列
        self.completed_tasks.append(task)
        del self.tasks[task_id]
        
        # 更新指标
        self.increment_counter("tasks_failed")
        self.increment_counter(f"error_{self._categorize_error(error_message)}")
        self.set_gauge("active_tasks", len(self.tasks))
        
        logger.error(f"任务失败: {task_id} - {error_message}")
        return task
    
    def _categorize_error(self, error_message: str) -> str:
        """错误分类"""
        error_lower = error_message.lower()
        
        if any(keyword in error_lower for keyword in ['memory', 'oom', 'cuda']):
            return "memory_error"
        elif any(keyword in error_lower for keyword in ['timeout', 'connection']):
            return "network_error"
        elif any(keyword in error_lower for keyword in ['format', 'codec', 'decode']):
            return "format_error"
        elif any(keyword in error_lower for keyword in ['model', 'load', 'whisper']):
            return "model_error"
        else:
            return "unknown_error"
    
    def increment_counter(self, name: str, value: int = 1):
        """增加计数器"""
        self.counters[name] += value
    
    def set_gauge(self, name: str, value: float):
        """设置仪表值"""
        self.gauges[name] = value
    
    def add_timer(self, name: str, value: float):
        """添加计时器值"""
        self.timers[name].append(value)
        # 保持最近的1000个值
        if len(self.timers[name]) > 1000:
            self.timers[name] = self.timers[name][-1000:]
    
    def get_statistics(self) -> Dict[str, Any]:
        """获取统计信息"""
        now = datetime.now()
        
        # 基础统计
        total_completed = len(self.completed_tasks)
        total_failed = self.counters.get("tasks_failed", 0)
        total_started = self.counters.get("tasks_started", 0)
        
        # 成功率
        success_rate = (total_completed / total_started * 100) if total_started > 0 else 0
        
        # 处理时间统计
        processing_times = self.timers.get("processing_time", [])
        avg_processing_time = sum(processing_times) / len(processing_times) if processing_times else 0
        
        # 置信度统计
        confidence_scores = self.timers.get("confidence_score", [])
        avg_confidence = sum(confidence_scores) / len(confidence_scores) if confidence_scores else 0
        
        # 最近1小时的任务
        recent_cutoff = now - timedelta(hours=1)
        recent_tasks = [task for task in self.completed_tasks 
                       if task.start_time >= recent_cutoff]
        
        # 系统性能
        latest_system_metrics = self.system_metrics[-1] if self.system_metrics else None
        
        return {
            "timestamp": now.isoformat(),
            "task_statistics": {
                "total_started": total_started,
                "total_completed": total_completed,
                "total_failed": total_failed,
                "active_tasks": len(self.tasks),
                "success_rate_percent": round(success_rate, 2),
                "recent_hour_tasks": len(recent_tasks)
            },
            "performance_statistics": {
                "avg_processing_time_seconds": round(avg_processing_time, 2),
                "avg_confidence_score": round(avg_confidence, 3),
                "total_processing_time_hours": round(sum(processing_times) / 3600, 2)
            },
            "error_statistics": dict(self.counters),
            "system_metrics": latest_system_metrics.to_dict() if latest_system_metrics else None,
            "active_tasks": [task.to_dict() for task in self.tasks.values()],
            "recent_completed_tasks": [task.to_dict() for task in recent_tasks[-10:]]
        }
    
    def get_health_status(self) -> Dict[str, Any]:
        """获取健康状态"""
        latest_metrics = self.system_metrics[-1] if self.system_metrics else None
        
        # 健康检查阈值
        cpu_threshold = 90
        memory_threshold = 90
        error_rate_threshold = 10  # 10%
        
        health_issues = []
        
        if latest_metrics:
            if latest_metrics.cpu_percent > cpu_threshold:
                health_issues.append(f"CPU使用率过高: {latest_metrics.cpu_percent:.1f}%")
            
            if latest_metrics.memory_percent > memory_threshold:
                health_issues.append(f"内存使用率过高: {latest_metrics.memory_percent:.1f}%")
        
        # 错误率检查
        total_started = self.counters.get("tasks_started", 0)
        total_failed = self.counters.get("tasks_failed", 0)
        error_rate = (total_failed / total_started * 100) if total_started > 0 else 0
        
        if error_rate > error_rate_threshold:
            health_issues.append(f"错误率过高: {error_rate:.1f}%")
        
        status = "healthy" if not health_issues else "warning"
        
        return {
            "status": status,
            "timestamp": datetime.now().isoformat(),
            "issues": health_issues,
            "metrics": {
                "cpu_percent": latest_metrics.cpu_percent if latest_metrics else None,
                "memory_percent": latest_metrics.memory_percent if latest_metrics else None,
                "error_rate_percent": round(error_rate, 2),
                "active_tasks": len(self.tasks)
            }
        }
    
    def export_metrics(self, filepath: str):
        """导出监控数据到文件"""
        try:
            data = {
                "export_time": datetime.now().isoformat(),
                "statistics": self.get_statistics(),
                "health_status": self.get_health_status(),
                "system_metrics_history": [m.to_dict() for m in list(self.system_metrics)],
                "completed_tasks_history": [t.to_dict() for t in list(self.completed_tasks)]
            }
            
            with open(filepath, 'w', encoding='utf-8') as f:
                json.dump(data, f, indent=2, ensure_ascii=False)
            
            logger.info(f"监控数据已导出到: {filepath}")
        except Exception as e:
            logger.error(f"导出监控数据失败: {e}")
    
    def start_system_metrics_collection(self):
        """启动系统指标收集"""
        if not self.monitoring_enabled:
            self.monitoring_enabled = True
            self._start_system_monitoring()
            logger.info("系统指标收集已启动")
        else:
            logger.info("系统指标收集已在运行中")
    
    def stop_system_metrics_collection(self):
        """停止系统指标收集"""
        self.monitoring_enabled = False
        logger.info("系统指标收集已停止")
    
    def stop_monitoring(self):
        """停止监控"""
        self.monitoring_enabled = False
        logger.info("性能监控器已停止")

# 全局监控实例
performance_monitor = PerformanceMonitor()

# 监控装饰器
def monitor_processing(func: Callable) -> Callable:
    """处理函数监控装饰器"""
    async def wrapper(*args, **kwargs):
        import uuid
        task_id = str(uuid.uuid4())
        
        # 从参数中提取信息
        file = kwargs.get('file') or (args[1] if len(args) > 1 else None)
        language = kwargs.get('language', 'unknown')
        model_name = kwargs.get('model_name', 'unknown')
        
        if file:
            filename = getattr(file, 'filename', 'unknown')
            file_size = getattr(file, 'size', 0)
        else:
            filename = 'unknown'
            file_size = 0
        
        # 开始监控
        task = performance_monitor.start_task(
            task_id=task_id,
            filename=filename,
            file_size=file_size,
            language=language,
            model=model_name
        )
        
        try:
            result = await func(*args, **kwargs)
            
            # 提取结果信息
            confidence = result.get('confidence') if isinstance(result, dict) else None
            text_length = len(result.get('text', '')) if isinstance(result, dict) else None
            
            performance_monitor.complete_task(task_id, confidence, text_length)
            return result
            
        except Exception as e:
            performance_monitor.fail_task(task_id, str(e))
            raise
    
    return wrapper

logger.info("监控模块已加载")